package org.onehippo.forge.konakart.cms.replication.factory;
import com.konakart.app.Product;
import com.konakart.appif.LanguageIf;
import eu.medsea.mimeutil.MimeUtil2;
import org.apache.commons.lang.StringUtils;
import org.hippoecm.frontend.plugins.gallery.processor.ScalingGalleryProcessor;
import org.hippoecm.frontend.plugins.gallery.processor.ScalingParameters;
import org.joda.time.DateTime;
import org.onehippo.forge.konakart.cms.replication.jcr.GalleryProcesssorConfig;
import org.onehippo.forge.konakart.cms.replication.utils.Codecs;
import org.onehippo.forge.konakart.cms.replication.utils.NodeHelper;
import org.onehippo.forge.konakart.cms.replication.utils.NodeImagesHelper;
import org.onehippo.forge.konakart.common.KKCndConstants;
import org.onehippo.forge.konakart.common.engine.KKStoreConfig;
import org.onehippo.forge.konakart.common.jcr.HippoModuleConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
/**
* To use the replication synchronization, you need to add a class that will extend this class to define
* the following information :
* - ProductDocType : Define the name of the document type which defines a product.
* - KonakartProductPropertyName : Define the name of the node which contains the konakart product
* <p/>
*/
public abstract class AbstractProductFactory implements ProductFactory {
private static Logger log = LoggerFactory.getLogger(AbstractProductFactory.class);
protected MimeUtil2 mimeUtil = new MimeUtil2();
protected Session session;
private NodeHelper nodeHelper;
private NodeImagesHelper nodeImagesHelper;
private String contentRoot;
private String galleryRoot;
private String productFolder;
protected AbstractProductFactory() {
// Register the mime detector
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
}
/**
* @return the name of the product folder
*/
public String getProductFolder() {
return productFolder;
}
/**
* @return the location where the images are stored
*/
public String getGalleryRoot() {
return galleryRoot;
}
/**
* @return the location where the content is stored
*/
public String getContentRoot() {
return contentRoot;
}
/**
* This is an helper method that could be used to set others information defined into Konakart but not
* already integrated within the konakart:konakart document.
*
* @param storeId the store id - Useful to retrieve the client engine
* @param product the konakart's product
* @param node the product's node
* @param language the product's language
*/
protected abstract void updateProperties(String storeId, Product product, Node node, LanguageIf language);
@Override
public void setSession(Session session) throws RepositoryException {
this.session = session;
nodeHelper = new NodeHelper(session);
nodeHelper.setFolderNodeTypeName(KKCndConstants.ECOMMERCE_DOC_TYPE);
nodeHelper.setFolderAdditionWorkflowCategory(KKCndConstants.NEW_PRODUCTS_FOLDER_TEMPLATE);
nodeHelper.setDocumentAdditionWorkflowCategory(KKCndConstants.NEW_PRODUCT_DOCUMENT_TEMPLATE);
nodeImagesHelper = new NodeImagesHelper(session);
}
@Override
public void setKKStoreConfig(KKStoreConfig kkStoreConfig) {
setContentRoot(kkStoreConfig.getContentRoot());
setGalleryRoot(kkStoreConfig.getGalleryRoot());
setProductFolder(kkStoreConfig.getProductFolder());
}
@Override
public boolean shouldAddProduct(Product product, LanguageIf language) {
// Bu default the product is added.
return true;
}
@Override
public void add(String storeId, Product product, LanguageIf language, String baseImagePath) throws Exception {
if (!shouldAddProduct(product, language)) {
log.info("The product " + product.getName() + " will not be added to Hippo");
return;
}
KKCndConstants.PRODUCT_TYPE product_type = KKCndConstants.PRODUCT_TYPE.findByType(product.getType());
String productDocType = HippoModuleConfig.getConfig().getClientEngineConfig().getProductNodeTypeMapping().get(product_type.getNamespace());
if (StringUtils.isEmpty(productDocType)) {
log.error("No product namespace has been associated for the product namespace : "
+ product_type.getNamespace() + ". Please set the it within the pluginconfig located " +
"at " + HippoModuleConfig.KONAKART_PRODUCT_TYPE_NAMESPACES_PATH);
return;
}
String absPath = contentRoot + "/" + Codecs.encodeNode(productFolder) + "/" + createProductNodeRoot(product);
// Create or retrieve the root folder
Node rootFolder;
// Does not exist - create it.
if (!session.getRootNode().hasNode(StringUtils.removeStart(absPath, "/"))) {
rootFolder = nodeHelper.createMissingFolders(absPath);
} else {
rootFolder = session.getNode(absPath);
}
// Create or retrieve the product node
Node productNode = nodeHelper.createOrRetrieveDocument(rootFolder, product, productDocType,
session.getUserID(), getLanguageToSetToHippoDoc(language));
boolean addNewProduct = !productNode.hasNode(KKCndConstants.PRODUCT_ID);
boolean hasCheckout = false;
// Check if the node is check-in
if (!productNode.isCheckedOut()) {
nodeHelper.checkout(productNode.getPath());
hasCheckout = true;
}
// Set the state of the product
String state = (product.getStatus() == 0) ? NodeHelper.UNPUBLISHED_STATE : NodeHelper.PUBLISHED_STATE;
nodeHelper.updateState(productNode, state);
// Update the node
updateProperties(storeId, product, productNode, language);
// Create the konakart ref product
createOrUpdateKonakartProduct(product, productNode);
// Upload images
uploadImages(language, productNode, baseImagePath, product);
// Save the session
productNode.getSession().save();
// Save the node
if (hasCheckout) {
nodeHelper.checkin(productNode.getPath());
}
if (addNewProduct) {
if (log.isDebugEnabled()) {
log.debug("The konakart product with id : {} has been added", product.getId());
}
} else {
if (log.isDebugEnabled()) {
log.debug("The konakart product with id : {} has been updated", product.getId());
}
}
}
/**
* Could have a more complex mapping between the Konakart's locale and the Hippo's locale set to the document
*
*
* @param locale the konakart locale
* @return the locale that will be set to the document
*/
protected String getLanguageToSetToHippoDoc(LanguageIf locale) {
// by default this method return the konakart's locale.
return locale.getLocale();
}
/**
* Create a unique page where the product will be created.
* <p/>
* It will create also the SEO URL of the product
*
* @param product a product
* @return a unique path where the product will be created
*/
protected String createProductNodeRoot(Product product) {
// Get the manufacturer name
String absPath = Codecs.encodeNode(product.getManufacturerName());
// Get the creation time
DateTime dateTime = new DateTime(product.getDateAdded().getTime().getTime());
absPath += "/" + dateTime.getYear() + "/" + dateTime.getMonthOfYear() + "/" + product.getName().substring(0, 1);
return absPath;
}
/**
* Create or update the konakart node.
*
* @param product the konakart product
* @param productNode the hippo product's node
* @throws javax.jcr.RepositoryException if any exception occurs
*/
private void createOrUpdateKonakartProduct(Product product, Node productNode) throws RepositoryException {
productNode.setProperty(KKCndConstants.PRODUCT_ID, product.getId());
}
/**
* Upload the images from Konakart to Hippo CMS
*
* @param language the current language
* @param productNode product node
* @param baseImagePath path where the konakart's images are located
* @param product the konakart product
*/
protected void uploadImages(LanguageIf language, Node productNode, String baseImagePath, Product product) {
try {
// Retrieve the gallery root node
// add the product node root
String galleryRootNode = galleryRoot + "/" + Codecs.encodeNode(productFolder) + "/" +
createProductNodeRoot(product) + "/" + product.getName() + "/" + product.getId();
// Get the root folder
Node productGalleryNode = nodeImagesHelper.createMissingFolders(galleryRootNode);
// Get the list of images to updload
Collection<String> images = getImagesByLanguage(product, language);
uploadImages(productNode, productGalleryNode, baseImagePath, images);
} catch (Exception e) {
log.error("Failed to add images", e);
}
}
/**
* Get the list of images to upload
* <p/>
* Konakart is not useful to manage images by language. In this case, the project needs to select
* which images will be used for a language.
* <p/>
* By default the language will be ignored. So all images set within Konakart will be retrieved
*
* @param product the product to add
* @param language the language associated to this product
* @return the list of images to upload
*/
public Collection<String> getImagesByLanguage(Product product, LanguageIf language) {
List<String> images = new ArrayList<String>();
if (StringUtils.isNotEmpty(product.getImage())) {
images.add(product.getImage());
}
if (StringUtils.isNotEmpty(product.getImage2())) {
images.add(product.getImage2());
}
if (StringUtils.isNotEmpty(product.getImage3())) {
images.add(product.getImage3());
}
if (StringUtils.isNotEmpty(product.getImage4())) {
images.add(product.getImage4());
}
return images;
}
/**
* Upload the image into the content's gallery
*
* @param productNode the product node. Used to associate the image with the product's node
* @param productGalleryNode the product gallery node
* @param baseImagePath the Konakart images' folder where the images are localed.
* @param productImages list of images to upload
* @throws javax.jcr.RepositoryException .
*/
private void uploadImages(Node productNode, Node productGalleryNode, String baseImagePath,
Collection<String> productImages) throws RepositoryException {
for (String productImage : productImages) {
String image = baseImagePath + "/" + productImage;
String productImageName = productImage;
if (StringUtils.contains(productImage, "/")) {
productImageName = StringUtils.substringAfterLast(productImage, "/");
}
File file = new File(image);
if (!file.exists()) {
if (!StringUtils.equals(file.getName(), "none.png")) {
log.warn("Failed to import image. The image at the path {} has not been found. ", image);
}
return;
}
Node rootImageNode = null;
try {
String contentType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file)).toString();
// Create the image name
rootImageNode = nodeImagesHelper.createGalleryItem(productGalleryNode, getImageNodeTypeName(), productImageName);
final ScalingGalleryProcessor processor = new ScalingGalleryProcessor();
// for each version, create a new image.
for (String imagesVersionName : GalleryProcesssorConfig.getConfig().getImagesVersionSet()) {
// Create the image's node if not exist
if (rootImageNode.hasNode(imagesVersionName)) {
rootImageNode.getNode(imagesVersionName).remove();
}
if (!rootImageNode.hasNode(imagesVersionName)) {
InputStream isStream = new FileInputStream(file);
GalleryProcesssorConfig.ImageConfig imageConfig =
GalleryProcesssorConfig.getConfig().getImageConfigMap(imagesVersionName);
ScalingParameters parameters = new ScalingParameters(imageConfig.getWidth(),
imageConfig.getHeight(), imageConfig.getUpscaling(),
imageConfig.getScalingStrategy(), imageConfig.getCompression());
processor.addScalingParameters(imagesVersionName, parameters);
Node node = rootImageNode.addNode(imagesVersionName, "hippogallery:image");
processor.initGalleryResource(node, isStream, contentType, productImageName, Calendar.getInstance());
}
}
} catch (FileNotFoundException e) {
// should not happends. Already verified.
} catch (RepositoryException e) {
log.warn("Unable to create the different versions of the original image - {} ", e);
}
// Save the image
if (rootImageNode != null) {
rootImageNode.getSession().save();
}
// Associate the image handle with the product
if (rootImageNode != null) {
linkImageToProduct(productNode, rootImageNode);
} else {
log.warn("Node has not been created for the image name - " + productImage);
}
}
}
/**
* Link the image to the product
* @param productNode the product
* @param rootImageNode the image to link
* @throws RepositoryException throw an exception if occurs.
*/
protected void linkImageToProduct(final Node productNode, final Node rootImageNode) throws RepositoryException {
Node imageLink = null;
// Retrieve the image UUID
String imageUUID = rootImageNode.getParent().getIdentifier();
// Check if the image node has been already added
if (productNode.hasNode(KKCndConstants.PRODUCT_IMAGES)) {
NodeIterator iterator = productNode.getNodes(KKCndConstants.PRODUCT_IMAGES);
while (iterator.hasNext()) {
Node node = iterator.nextNode();
if (node.hasProperty("hippo:docbase")) {
String docbase = node.getProperty("hippo:docbase").getString();
if (docbase.contains(imageUUID)) {
imageLink = node;
}
}
}
}
if (imageLink == null) {
imageLink = productNode.addNode(KKCndConstants.PRODUCT_IMAGES, "hippogallerypicker:imagelink");
}
imageLink.setProperty("hippo:docbase", imageUUID);
imageLink.setProperty("hippo:facets", new String[0]);
imageLink.setProperty("hippo:values", new String[0]);
imageLink.setProperty("hippo:modes", new String[0]);
}
/**
* @param contentRoot set the content root where the document will be created.
*/
private void setContentRoot(String contentRoot) {
this.contentRoot = StringUtils.removeEnd(contentRoot, "/");
}
/**
* @param galleryRoot set the gallery root where the images will be saved
*/
private void setGalleryRoot(String galleryRoot) {
this.galleryRoot = StringUtils.removeEnd(galleryRoot, "/");
}
/**
* @param productFolder set the name of the folder where the product will be created
*/
private void setProductFolder(String productFolder) {
this.productFolder = productFolder;
}
/**
* @return The name of the primary node type of the new image.
*/
public String getImageNodeTypeName() {
return "hippogallery:imageset";
}
}